#include "audio.h"
#include "app_data/log.h"
#include "media/libmad_driver.h"
#include <alsa/asoundlib.h>
#include <alsa/pcm.h>
#include <cstdio>
#include <linux/input.h>
#include <pthread.h>
#include <thread>
#include <unistd.h>

#define TAG "audio driver"

#define WAV_HEADER_SIZE 44
#define SAMPLE_PER_FRAME 2304
#define MAX_FRAMES 5    // 缓冲时间约为 MAX_FRAMES * 50ms

extern "C" int mp3DataProcessHandler(const short* pcm, size_t num_samples, int frq, int channels);

namespace {
    struct AudioFrame {
        short buf[SAMPLE_PER_FRAME];
        uint32_t length;
        uint32_t frequency;
        uint32_t channels;
    };

    struct AudioCache {
        AudioFrame frames[MAX_FRAMES];
        uint8_t head;
        uint8_t tail;
        uint8_t num;
    };

    struct WAVHeader {
        char riff[4];
        uint32_t file_size;
        char wave[4];
        char fmt[4];
        uint32_t fmt_size;
        uint16_t audio_format;
        uint16_t channels;
        uint32_t sample_rate;
        uint32_t byte_rate;
        uint16_t block_align;
        uint16_t bits_per_sample;
        char data[4];
        uint32_t data_size;
    };
}    // namespace

// Audio 类私有成员可以直接使用这些结构体
struct Audio::Impl {
public:
    Impl();

    int GetIdleNode();
    void ClearNode(SysAudioNode& node);
    int ReadFrameData(AudioFrame& frame);
    int InitWavDecoder(const std::string& file_path);
    int InitMp3Decoder(const std::string& file_path);

    void* PlayThread(void* arg);
    void* DecodeThread(void* arg);

    AudioCache audio_cache;
    WAVHeader wav_header;
    SysAudioNode sys_audio[AUDIO_CACHE_NUM];
    bool has_high_priority_task = false;

    AUDIO_PRIORITY_t decode_priority = AUDIO_PRIORITY_NONE;
    pthread_t play_thread;
    pthread_t decode_thread;
    pthread_mutex_t decode_mutex = PTHREAD_MUTEX_INITIALIZER;
    snd_pcm_t* alsa_handle;

    static void* ThreadEntry_Play(void* arg) {
        return static_cast<Audio::Impl*>(arg)->PlayThread(arg);
    }

    static void* ThreadEntry_Decode(void* arg) {
        return static_cast<Audio::Impl*>(arg)->DecodeThread(arg);
    }
};

static Audio::Impl* g_audio_impl = nullptr;

Audio::Audio() : impl_(std::make_unique<Impl>()) {}

Audio::~Audio() = default;

void Audio::AddNode(SysAudioNode& node) {
    int index = impl_->GetIdleNode();
    if (index < 0) {
        LOG_E(TAG, "No idle node");
        return;
    }

    for (int i = 0; i < AUDIO_CACHE_NUM; i++) {
        if (impl_->sys_audio[i].priority >= AUDIO_PRIORITY_CLICK && node.priority == AUDIO_PRIORITY_CLICK) {
            return;
        }
    }

    pthread_mutex_lock(&impl_->decode_mutex);
    if (node.priority > AUDIO_PRIORITY_CLICK) {
        for (int i = 0; i < AUDIO_CACHE_NUM; i++) {
            if (impl_->sys_audio[i].priority == AUDIO_PRIORITY_CLICK) {
                impl_->ClearNode(impl_->sys_audio[i]);
            }
        }
    }

    impl_->sys_audio[index] = node;
    if (impl_->decode_priority < node.priority) {
        impl_->has_high_priority_task = true;
    }

    pthread_mutex_unlock(&impl_->decode_mutex);
}

void Audio::AddMusicListNode(const std::vector<AudioNode>& nodes, AUDIO_PRIORITY_t priority, PlayEndCallback callback) {
    if (nodes.empty()) return;

    SysAudioNode sys_audio_node;
    sys_audio_node.priority = priority;
    sys_audio_node.play_end_callback = std::move(callback);
    sys_audio_node.num_nodes = static_cast<int>(nodes.size());
    sys_audio_node.audio_nodes = nodes;    // std::vector 自动深拷贝

    AddNode(sys_audio_node);    // 调用已有 AddNode 函数
}

void Audio::AddMusicListNode(std::initializer_list<AudioNode> nodes, AUDIO_PRIORITY_t priority, PlayEndCallback callback) {
    AddMusicListNode(std::vector<AudioNode>{ nodes }, priority, std::move(callback));
}

Audio::Impl::Impl() {
    int err;
    const char* device = "hw:audiocodec";

    // 打开 ALSA PCM 设备
    if ((err = snd_pcm_open(&alsa_handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
        LOG_I(TAG, "Playback open error: %s\n", snd_strerror(err));
        return;
    }

    snd_pcm_hw_params_t* hw_params;
    snd_pcm_hw_params_malloc(&hw_params);
    snd_pcm_hw_params_any(alsa_handle, hw_params);
    snd_pcm_hw_params_set_access(alsa_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(alsa_handle, hw_params, SND_PCM_FORMAT_S16_LE);
    snd_pcm_hw_params_set_channels(alsa_handle, hw_params, 2);    // 默认立体声
    unsigned int rate = 44100;
    snd_pcm_hw_params_set_rate_near(alsa_handle, hw_params, &rate, nullptr);
    snd_pcm_uframes_t buffer_size = SAMPLE_PER_FRAME;
    snd_pcm_hw_params_set_buffer_size_near(alsa_handle, hw_params, &buffer_size);
    if ((err = snd_pcm_hw_params(alsa_handle, hw_params)) < 0) {
        LOG_I(TAG, "Unable to set hw params: %s\n", snd_strerror(err));
    }
    snd_pcm_hw_params_free(hw_params);
    snd_pcm_prepare(alsa_handle);

    // 启动线程（使用 pthread）
    pthread_attr_t attr;
    struct sched_param param;

    pthread_attr_init(&attr);
    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    pthread_attr_setschedpolicy(&attr, SCHED_FIFO);

    param.sched_priority = sched_get_priority_max(SCHED_FIFO);
    pthread_attr_setschedparam(&attr, &param);
    pthread_create(&play_thread, &attr, &Audio::Impl::ThreadEntry_Play, this);

    param.sched_priority -= 1;
    pthread_attr_setschedparam(&attr, &param);
    pthread_create(&decode_thread, &attr, &Audio::Impl::ThreadEntry_Decode, this);

    LOG_I(TAG, "Audio driver initialized (C++ style).\n");
}

void Audio::Impl::ClearNode(SysAudioNode& node) {
    node.num_nodes = 0;
    node.priority = AUDIO_PRIORITY_NONE;
    node.play_end_callback = nullptr;
    node.audio_nodes.clear();
}

int Audio::Impl::GetIdleNode() {
    for (int i = 0; i < AUDIO_CACHE_NUM; i++) {
        if (sys_audio[i].num_nodes == 0) {
            return i;
        }
    }
    return -1;
}

int Audio::Impl::ReadFrameData(AudioFrame& frame) {
    if (audio_cache.num == 0) {
        return 0;
    }

    frame = audio_cache.frames[audio_cache.head];
    audio_cache.head = (audio_cache.head + 1) % MAX_FRAMES;
    audio_cache.num--;
    return 1;
}

int Audio::Impl::InitWavDecoder(const std::string& file_path) {
    FILE* fp = fopen(file_path.c_str(), "rb");
    if (fp == nullptr) {
        LOG_E(TAG, "open file failed");
        return -1;
    }

    // 读取文件头
    if (fread(&wav_header, 1, sizeof(WAVHeader), fp) != sizeof(WAVHeader)) {
        LOG_E(TAG, "read wav header failed");
        fclose(fp);
        return -1;
    }

    // 检查文件头
    if (strncmp(wav_header.riff, "RIFF", 4) != 0 || strncmp(wav_header.wave, "WAVE", 4) != 0) {
        LOG_E(TAG, "invalid wav file");
        fclose(fp);
        return -1;
    }

    std::fseek(fp, sizeof(WAVHeader), SEEK_SET);

    while (1) {
        while (audio_cache.num >= MAX_FRAMES || has_high_priority_task) {
            if (has_high_priority_task) {
                LOG_I(TAG, "New audio has higher priority, stop decoding.\n");
                fclose(fp);
                return -1;
            }
            usleep(20000);
        }

        size_t bytes_read = fread(audio_cache.frames[audio_cache.tail].buf, 2, SAMPLE_PER_FRAME, fp);
        if (bytes_read == 0) {
            if (feof(fp)) {
                fclose(fp);
                return 0;
            } else {
                LOG_I(TAG, "Failed to read data frame");
                fclose(fp);
                return -1;
            }
        }

        audio_cache.frames[audio_cache.tail].length = bytes_read;
        audio_cache.frames[audio_cache.tail].frequency = wav_header.sample_rate;
        audio_cache.frames[audio_cache.tail].channels = wav_header.channels;
        audio_cache.tail = (audio_cache.tail + 1) % MAX_FRAMES;
        audio_cache.num++;
    }
}

int Audio::Impl::InitMp3Decoder(const std::string& file_path) {
    g_audio_impl = this;

    Libmad_driver::getInstance().initialize(file_path.c_str(), mp3DataProcessHandler);
    return 0;
}

extern "C" int mp3DataProcessHandler(const short* pcm, size_t num_samples, int frq, int channels) {
    if (g_audio_impl == nullptr) {
        return -1;
    }

    auto& audio_cache = g_audio_impl->audio_cache;
    auto& has_high_priority_task = g_audio_impl->has_high_priority_task;

    while (audio_cache.num >= MAX_FRAMES || has_high_priority_task) {
        if (has_high_priority_task) {
            LOG_I(TAG, "New audio has higher priority, stop decoding.\n");
            return -1;
        }
        usleep(20000);
    }

    memcpy(audio_cache.frames[audio_cache.tail].buf, pcm, num_samples * sizeof(short));
    audio_cache.frames[audio_cache.tail].length = num_samples;
    audio_cache.frames[audio_cache.tail].frequency = frq;
    audio_cache.frames[audio_cache.tail].channels = channels;
    audio_cache.tail = (audio_cache.tail + 1) % MAX_FRAMES;
    audio_cache.num++;
    return 0;
}

void* Audio::Impl::PlayThread(void* arg) {
    AudioFrame frame;
    uint32_t frequency = 44100, channels = 2;

    while (1) {
        if (ReadFrameData(frame)) {
            if (frequency != frame.frequency || channels != frame.channels) {
                frequency = frame.frequency;
                channels = frame.channels;
                LOG_I(TAG, "sample rate: %d, channels: %d", frequency, channels);

                snd_pcm_drain(alsa_handle);
                snd_pcm_hw_params_t* hw_params;
                snd_pcm_hw_params_malloc(&hw_params);
                snd_pcm_hw_params_any(alsa_handle, hw_params);
                snd_pcm_hw_params_set_access(alsa_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
                snd_pcm_hw_params_set_format(alsa_handle, hw_params, SND_PCM_FORMAT_S16_LE);
                snd_pcm_hw_params_set_channels(alsa_handle, hw_params, frame.channels);
                snd_pcm_hw_params_set_rate_near(alsa_handle, hw_params, &frame.frequency, 0);
                snd_pcm_uframes_t buffer_size = frame.length;
                snd_pcm_hw_params_set_buffer_size_near(alsa_handle, hw_params, &buffer_size);
                int err = snd_pcm_hw_params(alsa_handle, hw_params);
                if (err < 0) {
                    LOG_I(TAG, "Failed to set hw params: %s\n", snd_strerror(err));
                }
                snd_pcm_hw_params_free(hw_params);

                // 准备设备
                snd_pcm_prepare(alsa_handle);
            }
            if (frame.length == 0) {
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
                continue;
            }
            int ret = snd_pcm_writei(alsa_handle, frame.buf, frame.length / frame.channels);
            if (ret < 0) {
                snd_pcm_prepare(alsa_handle);
                ret = snd_pcm_writei(alsa_handle, frame.buf, frame.length / frame.channels);
                if (ret < 0) {
                    LOG_I(TAG, "Failed to write data: %s\n", snd_strerror(ret));
                }
            }
        } else {
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
        }
    }

    return nullptr;
}

void* Audio::Impl::DecodeThread(void* arg) {
    int node_index = -1;
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));

    while (1) {
        pthread_mutex_lock(&decode_mutex);
        decode_priority = AUDIO_PRIORITY_NONE;
        has_high_priority_task = false;

        for (int i = 0; i < AUDIO_CACHE_NUM; i++) {
            if (decode_priority < sys_audio[i].priority) {
                decode_priority = sys_audio[i].priority;
                node_index = i;
            }
        }
        pthread_mutex_unlock(&decode_mutex);
        if (decode_priority == AUDIO_PRIORITY_NONE) {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            continue;
        }

        for (int i = 0; i < sys_audio[node_index].num_nodes && !has_high_priority_task; i++) {
            if (sys_audio[node_index].audio_nodes[i].type == AUDIO_TYPE_WAV) {
                InitWavDecoder(sys_audio[node_index].audio_nodes[i].file_path);
            } else if (sys_audio[node_index].audio_nodes[i].type == AUDIO_TYPE_MP3) {
                InitMp3Decoder(sys_audio[node_index].audio_nodes[i].file_path);
            }
        }
        if (sys_audio[node_index].play_end_callback) {
            sys_audio[node_index].play_end_callback();
        }
        ClearNode(sys_audio[node_index]);
    }

    return nullptr;
}